/*
 * Copyright 2009 Lukasz Wozniak
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 *
 * http://www.apache.org/licenses/LICENSE-2.0 
 *
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */
package pl.lwozniak.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.Random;

import pl.lwozniak.blip.message.BlipMessagePart;
import pl.lwozniak.blip.message.FileMessagePart;
import pl.lwozniak.blip.message.TextMessagePart;

/**
 * Helper class for generating content of HTTP request.
 * 
 * @author Lukasz Wozniak
 *
 */
public class HttpBodyGenerator{

	private static final String UTF_8 = "UTF-8";

	public void writeBody(HttpURLConnection con, BlipMessagePart body)
			throws IOException {
		if (allText(body)) {
			con.setRequestProperty(HttpStrings.CONTENT_TYPE,
					HttpStrings.APPLICATION_X_WWW_FORM_URLENCODED);
			con.setDoOutput(true);
			OutputStream os = con.getOutputStream();
			writeUrlEncoded(os, body);
			os.flush();
			os.close();
		} else {
			String boudary = generateBoundary();
			con.setRequestProperty(HttpStrings.CONTENT_TYPE,
					HttpStrings.MULTIPART_FORM_DATA_BOUDARY + boudary);
			con.setDoOutput(true);
			OutputStream os = con.getOutputStream();
			for (BlipMessagePart part : body) {
				writePart(os, part, boudary);
				os.flush();
			}
			os.write(HttpStrings.DOUBLE_DASH);
			os.write(boudary.getBytes(UTF_8));
			os.write(HttpStrings.DOUBLE_DASH);
			os.write(HttpStrings.CRLF);
			os.flush();
			os.close();
		}
	}
	
	/**
	 * Writes whole body as form-urlencoded data.
	 * 
	 * @param os
	 * @param body
	 * @throws IOException 
	 */
	private void writeUrlEncoded(OutputStream os, BlipMessagePart body) throws IOException {
		Iterator<BlipMessagePart> i = body.iterator();
		while(i.hasNext()){
			BlipMessagePart part = i.next();
			os.write(URLEncoder.encode(part.getName(),UTF_8).getBytes(UTF_8));
			os.write(HttpStrings.EQUALS.getBytes(UTF_8));
			os.write(URLEncoder.encode(((TextMessagePart)part).getText(),UTF_8).getBytes(UTF_8));
			if(i.hasNext()){
				os.write(HttpStrings.AMPERSAND.getBytes(UTF_8));
			}
		}
		
	}

	/**
	 * Checks if all content is text and qualifies for being posted as <code>application/x-www-form-urlencoded</code>
	 * @param body
	 * @return
	 */
	private boolean allText(BlipMessagePart body) {
		for (BlipMessagePart blipMessagePart : body) {
			if(!(blipMessagePart instanceof TextMessagePart)){
				return false;
			}
		}
		return true;
	}

	private void writePart(OutputStream os, BlipMessagePart part, String boudary) throws IOException {
		os.write(HttpStrings.DOUBLE_DASH);
		os.write(boudary.getBytes(UTF_8));
		os.write(HttpStrings.CRLF);
		writePartHeader(os,part);
		os.write(HttpStrings.CRLF);
		copyStreams(part.getStream(), os);
		os.write(HttpStrings.CRLF);
	}

	private void writePartHeader(OutputStream os, BlipMessagePart part) throws IOException {
		os.write(HttpStrings.DISPOSITION.getBytes(UTF_8));
		os.write(HttpStrings.COLON.getBytes(UTF_8));
		os.write(HttpStrings.SPACE.getBytes(UTF_8));
		os.write(HttpStrings.FORM_DATA.getBytes(UTF_8));
		os.write(HttpStrings.SEMICOLON.getBytes(UTF_8));
		os.write(HttpStrings.SPACE.getBytes(UTF_8));
		os.write(HttpStrings.NAME.getBytes(UTF_8));
		os.write(HttpStrings.EQUALS.getBytes(UTF_8));
		os.write(HttpStrings.QUOTE.getBytes(UTF_8));
		os.write(part.getName().getBytes(UTF_8));
		os.write(HttpStrings.QUOTE.getBytes(UTF_8));
		if(part instanceof FileMessagePart){
			os.write(HttpStrings.SEMICOLON.getBytes(UTF_8));
			os.write(HttpStrings.SPACE.getBytes(UTF_8));
			os.write(HttpStrings.FILENAME.getBytes(UTF_8));
			os.write(HttpStrings.EQUALS.getBytes(UTF_8));
			os.write(HttpStrings.QUOTE.getBytes(UTF_8));
			os.write(((FileMessagePart) part) .getFilename().getBytes(UTF_8));
			os.write(HttpStrings.QUOTE.getBytes(UTF_8));
			//content transfer
			os.write(HttpStrings.CRLF);
			os.write(HttpStrings.CONTENT_TRANSFER_ENCODING.getBytes(UTF_8));
			os.write(HttpStrings.COLON.getBytes(UTF_8));
			os.write(HttpStrings.SPACE.getBytes(UTF_8));
			os.write(HttpStrings.ENC_BINARY.getBytes(UTF_8));
			//size
			if(((FileMessagePart) part).getFileSize()>0){
				os.write(HttpStrings.CRLF);
				os.write(HttpStrings.CONTENT_LENGTH.getBytes(UTF_8));
				os.write(HttpStrings.COLON.getBytes(UTF_8));
				os.write(HttpStrings.SPACE.getBytes(UTF_8));
				os.write(Long.toString(((FileMessagePart) part).getFileSize()).getBytes(UTF_8));
			}
		}
		os.write(HttpStrings.CRLF);
		if(!(part instanceof TextMessagePart)){
			os.write(HttpStrings.CONTENT_TYPE.getBytes(UTF_8));
			os.write(HttpStrings.COLON.getBytes(UTF_8));
			os.write(HttpStrings.SPACE.getBytes(UTF_8));
			if(part.getContentType()==null){
				os.write(HttpStrings.APPLICATION_OCTET_STREAM.getBytes(UTF_8));
			}else{
				os.write(part.getContentType().getBytes(UTF_8));
			}
			os.write(HttpStrings.CRLF);
		}
	}

	private void copyStreams(InputStream in, OutputStream out) throws IOException{
		byte[] buf = new byte[256];
		int cnt = in.read(buf);
		while(cnt>0){
			out.write(buf, 0, cnt);
			cnt = in.read(buf);
		}
	}
	
	private String generateBoundary(){
		Random random = new Random();
		StringBuilder sb = new StringBuilder();
		sb.append(HttpStrings.DOUBLE_DASHString);
		sb.append(HttpStrings.DOUBLE_DASHString);
		sb.append(HttpStrings.DOUBLE_DASHString);
		sb.append("bb");
		sb.append(HttpStrings.DOUBLE_DASHString);
		sb.append(Long.toHexString(random.nextLong()));
		return sb.toString();
	}
	
}
